package com.broada.apm.util;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;

import com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;

public class ApidocGenerate {
    public static class Paths {
        Map<String, Api> paths = new HashMap<String, ApidocGenerate.Api>();
    }

    public static class Api {
        Operation get;
        Operation post;
        Operation delete;
        Operation put;
    }

    public static class Operation {
        String method;
        String summary;
        String description = "";
        List<Parameter> parameters = new ArrayList<ApidocGenerate.Parameter>();
        List<String> tags = new ArrayList<String>();
    }

    public static class Parameter {
        String name;
        String in;
        boolean required;
        String type;
        String description;
        String format;
    }
    public static class Swagger {
        String swagger = "2.0";
        Map<String,String> info = new HashMap<String, String>();
        String host = "127.0.0.1:8080";
        String[] schemes = {"http"};
        String basePath = "/front/rest/";
        String[] produces = {"application/json"}; 
        Map<String, Api> paths;
    }
    private static void output(Writer wr, Paths paths) throws JsonGenerationException, JsonMappingException, IOException {
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
        om.setSerializationInclusion(Include.NON_NULL);
        om.enable(SerializationFeature.INDENT_OUTPUT);
        Swagger swagger = new Swagger();
        swagger.paths = paths.paths;
        om.writeValue(wr,swagger);
    }
    public static void scanClasses(Writer r, Class<?> ...clzs) throws IOException {
        Paths paths = new Paths();
        scanClasses(paths, clzs);
        output(r, paths);
    }
    private static void scanClasses(Paths paths, Class<?>... clzs) {
        for (Class<?> clz : clzs) {
            scanClass(paths, clz);
        }
    }
    private static void scanClass(Paths paths, Class<?> clz) {
        Path rootpath = clz.getAnnotation(Path.class);
        if (rootpath != null) {
            Path root = rootpath;
            Method[] methods = clz.getMethods();
            Api api = null;
            for (Method method : methods) {
                Path mp = method.getAnnotation(Path.class);
                String path = root.value() + (mp == null ? "/" : mp.value());
                api = (paths.paths.getOrDefault(path, new Api()));
                paths.paths.put(path, api);

                Operation oper = new Operation();
                oper.tags.add(clz.getName());
                ApiInfo info = method.getAnnotation(ApiInfo.class);
                oper.description = "实现类:"+clz.getCanonicalName() + "<br/>方法:" + method.getName();
                if (info != null) {
                    oper.summary = info.value();
                }
                java.lang.reflect.Parameter[] parameters = method.getParameters();
                for (java.lang.reflect.Parameter p : parameters) {
                    ApiInfo annotation = p.getAnnotation(ApiInfo.class);
                    Parameter param = new Parameter();
                    String paramName = p.getName();
                    if(annotation!=null && annotation.value() != null) {
                        paramName =  annotation.value(); 
                    } 
                    if(p.getAnnotation(QueryParam.class)!=null) {
                        paramName = p.getAnnotation(QueryParam.class).value();
                    }
                    param.name = paramName;
                    param.description = annotation != null ? annotation.desc(): "";
                    param.in = "query";
                    param.type = p.getType().getSimpleName().toLowerCase();
                    oper.parameters.add(param);
                }

                GET get = method.getAnnotation(GET.class);
                if (get != null) {
                    api.get = oper;
                }
                POST post = method.getAnnotation(POST.class);
                if (post != null) {
                    api.post = oper;
                }
                DELETE delete = method.getAnnotation(DELETE.class);
                if (delete != null) {
                    api.delete = oper;
                }
                PUT put = method.getAnnotation(PUT.class);
                if (put != null) {
                    api.put = oper;
                }

            }
        }
    }
}
